package framework.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import framework.exceptions.JsonException;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.DateFormat;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

public class JsonUtil {

    public final static List<Module> customModules = new CopyOnWriteArrayList<>();

    public static ObjectMapper mapper = newMapper(false);

    /**
     * 创建一个常规对象处理器
     *
     * @return
     */
    public static ObjectMapper newMapper() {
        return newMapper(false);
    }

    /**
     * 创建一个常规对象处理器
     *
     * @return
     */
    public static ObjectMapper newMapper(boolean registerJavaTime) {
        ObjectMapper mapper = new ObjectMapper();
        // 如果json中有新增的字段并且是实体类类中不存在的，不报错
        mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        // 如果存在未知属性，则忽略不报错
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//        // 允许key没有双引号
//        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
//        // 允许key有单引号
//        mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        // 配置将日期时间类型序列化为字符串形式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // register java.time
        if (registerJavaTime) {
            mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
        }
        //
        for (Module module : customModules) {
            mapper.registerModule(module);
        }
        return mapper;
    }

    /**
     * 对象转为json字符串
     *
     * @param obj 待转换对象
     * @param <T> 待转换对象类型
     * @return json格式字符串
     */
    public static <T> String toJsonString(T obj) {
        return toJsonString(obj, true);
    }

    /**
     * 对象转为json字符串
     *
     * @param obj    待转换对象
     * @param pretty 是否格式化
     * @param <T>    待转换对象类型
     * @return json格式字符串
     */
    public static <T> String toJsonString(T obj, boolean pretty) {
        if (Objects.isNull(obj)) {
            return null;
        }
        try {
            return pretty ? mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj) : mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new JsonException("Parse Object to String error", e);
        }
    }


    /**
     * 对象转为json字符串
     *
     * @param obj 待转换对象
     * @param <T> 待转换对象类型
     * @return json格式字符串
     */
    public static <T> String toJsonString(T obj, DateFormat dateFormat) {
        return toJsonString(obj, dateFormat, true);
    }

    /**
     * 对象转为json字符串
     *
     * @param obj 待转换对象
     * @param <T> 待转换对象类型
     * @return json格式字符串
     */
    public static <T> String toJsonString(T obj, DateFormat dateFormat, boolean pretty) {
        ObjectMapper mapper = newMapper();
        //mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
        mapper.setDateFormat(dateFormat);
        //
        String s;
        try {
            if (pretty)
                s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            else
                s = mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new JsonException("Parse Object to String error", e);
        }
        //
        return s;
    }

    /**
     * 转换json字符串为json对象
     *
     * @param text  待转换文本
     * @param clazz 转换为对象类型
     * @param <T>   换对象类型
     * @return 对象
     */
    public static <T> T parse(String text, Class<T> clazz) {
        if (!StringUtils.hasText(text)) return null;
        if (clazz == null) return null;
        if (clazz.equals(String.class)) return null;
        try {
            return mapper.readValue(text, clazz);
        } catch (IOException e) {
            throw new JsonException("Parse String to Object error", e);
        }
    }

    /**
     * 转换json字符串为json对象
     *
     * @param text          待转换文本
     * @param typeReference 转换为对象类型
     * @param <T>           换对象类型
     * @return 对象
     */
    public static <T> T parse(String text, TypeReference<T> typeReference) {
        if (!StringUtils.hasText(text)) return null;
        if (typeReference == null) return null;
        try {
            return mapper.readValue(text, typeReference);
        } catch (IOException e) {
            throw new JsonException("Parse String to Object error", e);
        }
    }

    /**
     * 转换json字符串为泛型对象
     *
     * @param text 待转换文本
     * @param <T>  换对象类型
     * @return 对象
     */
    public static <T> T parse(String text) {
        if (!StringUtils.hasText(text)) return null;
        try {
            return mapper.readValue(text, new TypeReference<T>() {
            });
        } catch (IOException e) {
            throw new JsonException("Parse String to Object error", e);
        }
    }

    /**
     * 转换json字符串为对象集合
     *
     * @param text  待转换文本
     * @param clazz 转换为对象类型
     * @param <T>   转换对象类型
     * @return 对象集合
     */
    public static <T> List<T> parseArray(String text, Class<T> clazz) {
        if (!StringUtils.hasText(text) || clazz == null) {
            return null;
        }
        JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, clazz);
        try {
            return mapper.readValue(text, javaType);
        } catch (IOException e) {
            throw new JsonException("Parse String to Array error", e);
        }
    }

    /**
     * 转换json字符串为对象,并指定时间转换格式
     *
     * @param text       待转换文本
     * @param <T>        换对象类型
     * @param dateFormat
     * @return 对象
     */
    public static <T> T parse(String text, DateFormat dateFormat) {
        if (!StringUtils.hasText(text)) return null;
        ObjectMapper mapper = newMapper();
        mapper.setDateFormat(dateFormat);
        try {
            return mapper.readValue(text, new TypeReference<T>() {
            });
        } catch (IOException e) {
            throw new JsonException("Parse String to Object error", e);
        }
    }

    /**
     * 转换json字符串为对象,并指定时间转换格式
     *
     * @param text       待转换文本
     * @param <T>        换对象类型
     * @param dateFormat
     * @return 对象
     */
    public static <T> T parse(String text, DateFormat dateFormat, TypeReference<T> typeReference) {
        if (!StringUtils.hasText(text)) return null;
        ObjectMapper mapper = newMapper();
        mapper.setDateFormat(dateFormat);
        try {
            return mapper.readValue(text, typeReference);
        } catch (IOException e) {
            throw new JsonException("Parse String to Object error", e);
        }
    }

    /**
     * 转换json字符串为对象集合,并指定时间转换格式
     *
     * @param text       待转换文本
     * @param clazz      转换为对象类型
     * @param <T>        转换对象类型
     * @param dateFormat
     * @return 对象集合
     */
    public static <T> List<T> parseArray(String text, DateFormat dateFormat, Class<T> clazz) {
        if (!StringUtils.hasText(text) || clazz == null) {
            return null;
        }
        ObjectMapper mapper = newMapper();
        mapper.setDateFormat(dateFormat);
        JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, clazz);
        try {
            return mapper.readValue(text, javaType);
        } catch (IOException e) {
            throw new JsonException("Parse String to Array error", e);
        }
    }

}
